/*
 ============================================================================
 Name        : filozofowie.c
 Description : Projekt zaliczeniowy na przetwarzanie rozproszone - pijący filozofowie
 ============================================================================
 */

#include "filozofowie.h"

void giveBottle() {
	int i, msg[5];

	msg[0] = 0;

	printf("(%d) : zwalniam butelki\n", rank);
	for (i = 0; i < size; i++) {
		//	printf("WP[%d]: %d\n", i, waitingPhilosophers[i]);
		if (waitingPhilosophers[i] == true) {
			logic_clock++;
			printf("Wpisywane ID: %d\n", waitingPhilosophersMessageID[i]);
			msg[1] = waitingPhilosophersMessageID[i];
			printf("wysylam z id: %d rank: %d\n",
					waitingPhilosophersMessageID[i],
					waitingPhilosophersRank[i]);
			msg[2] = waitingPhilosophersRank[i];
			msg[3] = 0; //nie daję zgody na puchar, bo piję wode
			msg[4] = 1; //daję zgodę na butelkę, bo już sobie nalałem
			printf("(%d) W sekcji, zgoda do oczekujacego - butelka (%d)\n",
					rank, i);
			MPI_Send(&msg, 5, MPI_INT, i, MSGTAG, MPI_COMM_WORLD);
			//waitingPhilosophers[i] = false;
		}

	}
}

void sendToWaiting() {

	int i, msg[5];

	msg[0] = 0;

	printf("(%d) : Rozpoczynam wysylanie oczekującym - zwalniam puchar\n",
			rank);
	for (i = 0; i < size; i++) {
		printf("WP[%d]: %d\n", i, waitingPhilosophers[i]);
		if (waitingPhilosophers[i] == true) {
			logic_clock++;
			printf("Wpisywane ID: %d\n", waitingPhilosophersMessageID[i]);
			msg[1] = waitingPhilosophersMessageID[i];
			printf("wysylam z id: %d rank: %d\n",
					waitingPhilosophersMessageID[i],
					waitingPhilosophersRank[i]);
			msg[2] = waitingPhilosophersRank[i];
			msg[3] = 1; //daję zgodę na puchar
			if (waintingPhilosophersDeeper[i] == true) {
				msg[4] = 1;
			} else {
				msg[4] = 0; //nie daję zgody na butelkę, bo dałem ją prędzej
			}
			printf("(%d) Po sekcji, zgoda do oczekujacego - puchar(%d)\n", rank,
					i);
			MPI_Send(&msg, 5, MPI_INT, i, MSGTAG, MPI_COMM_WORLD);
			waitingPhilosophers[i] = false;
		}

	}
}

//ustap pierwszenstwa
void giveWayToOther(int dest, int originRank, int messageID) {

	int msg[5];

	printf("(%d) : Ustepuje miejsca  %d\n", rank, dest);
	fflush(stdout);
	sleep(DELAY);
	msg[0] = 0;
	msg[1] = messageID;
	msg[2] = originRank;
	msg[3] = 1;
	msg[4] = 1;
	logic_clock++;
	MPI_Send(&msg, 5, MPI_INT, dest, MSGTAG, MPI_COMM_WORLD);

}

//ustal, ktory z chetnych procesow ma uzyskac zgode
void handleRequest(int messageID, int originRank, int src) {

	printf("(%d) handleRequest %d\n", rank, originRank);
	if (inside) {
		printf(
				"(%d) : Dodaje (%d) do kolejki oczekujacych-jestem w sekcji krytycznej\n",
				rank, src);
		waitingPhilosophers[src] = true;
		waitingPhilosophersMessageID[src] = messageID;
		waitingPhilosophersRank[src] = originRank;
		waintingPhilosophersDeeper[src] = deeper;
	} else {
		printf("DEBUG messageID: %d  queueNO: %d  src: %d  rank: %d\n\n",
				messageID, queueNo, src, rank);
		if ((messageID < queueNo) || ((messageID == queueNo) && (src < rank))) {
			//ustap pierwszenstwa
			printf("(%d) : Ustepuje %d - (%d/%d)\n", rank, src, queueNo,
					messageID);
			giveWayToOther(src, originRank, messageID);

		} else {
			//nalezy wrzucic nadawce do listy oczekujacych jesli to my mamy pierwszenstwo
			printf(
					"(%d) : Dodaje (%d) do kolejki oczekujacych - mam pierwszenstwo (%d/%d)\n",
					rank, src, queueNo, messageID);
			waitingPhilosophers[src] = true;
			waitingPhilosophersMessageID[src] = messageID;
			waitingPhilosophersRank[src] = originRank;
			printf("UWAGA UWAGA: %d\n", waitingPhilosophersMessageID[src]);
			waintingPhilosophersDeeper[src] = deeper;
		}
	}

}

void receiveAgreement(int messageID, int originRank, int src, int cup,
		int bottle) {

	if (originRank == rank && messageID == queueNo) {
		if (cup == 1) {
			agreeCountCup++;
			printf("ZGODA NA PUCHAR\n");
		}
		if (bottle == 1) {
			agreeCountBottle++;
			printf("ZGODA NA BUTELKE\n");
		}
	}
	printf("(%d) : Odebralem zgode od %d %d %d %d\n", rank, src, originRank,
			messageID, queueNo);
	printf("AgreeCountCup: %d, AgreeCountBottle: %d\n", agreeCountCup,
			agreeCountBottle);
	fflush(stdout);
	sleep(DELAY);

}

void criticalSection() {

	inside = true;
	printf("Piję wode, mam butelke i kielich\n");
	fflush(stdout);
	printf("oddaje butelke");
	sleep(DELAY);
	giveBottle();
	deeper = true;
	sleep(DELAY);
	agreeCountCup = 0;
	agreeCountBottle = 0;
	printf("(%d) Koniec sekcji krytycznej\n", rank);
	fflush(stdout);

	sendToWaiting();

	want = false;
	deeper = false;
	inside = false;
	sendToWaiting();

}

void allowToComeIn(int messageID, int dest) {

	int msg[5];

	msg[0] = 0; //zero, bo to jest zgoda
	msg[1] = messageID;
	msg[2] = dest;
	msg[3] = 1; //zezwolenie - puchar
	msg[4] = 1; //zezwolenie - kielich
	printf("allowToComeIn ID: %d\n", msg[1]);
	printf("(%d) Odbieram prosbe od %d\n", rank, dest);
	fflush(stdout);
	sleep(DELAY);
	logic_clock++;
	MPI_Send(&msg, 5, MPI_INT, dest, MSGTAG, MPI_COMM_WORLD);
	printf("(%d): Wysylam zgode do %d\n", rank, dest);
	fflush(stdout);
	sleep(DELAY);

}

void *receiver() {

	MPI_Status status;
	int msg[5];

	while (1) {

		MPI_Recv(&msg, 5, MPI_INT, MPI_ANY_SOURCE, MSGTAG, MPI_COMM_WORLD,
				&status);

		logic_clock = max(logic_clock, msg[1]) + 1;
		randomState = false;
		//jesli  odebralismy prosbe o zgode i nie chcemy wejsc ->wysylamy zgode
		if (want == false && msg[0] == -1) {
			allowToComeIn(msg[1], status.MPI_SOURCE);

		}
		//jesli chcemy wejsc
		else if (want == true) {
			//jesli zgoda
			if (msg[0] != -1) {
				receiveAgreement(msg[1], msg[2], status.MPI_SOURCE, msg[3],
						msg[4]);

			}
			//jesli ktos inny tez chce uzyskac dostep
			else {
				handleRequest(msg[1], msg[2], status.MPI_SOURCE);
			}

		}
		if (want == false)
			randomState = true;
	}
	return (NULL);
}

bool wantToTake() {

	int temp = rand() % 10 + 1;
	if (temp > chanceToDrink)
		return true;
	return false;

}

void tryToDrink() {
	sendRequests();
	while (agreeCountCup != requiredAgreementCups)
		;
	while (agreeCountBottle < requiredAgreementBottles)
		;


	printf("(%d) Mam zgody \n", rank);

	criticalSection();
	randomState = true;

}

void sendRequests() {
	//wyslij prosby o zgody do wszystkich

//	MPI_Status status;
	int i, msg[5];

	//wyslij do wszystkich prosbe o zgode
	msg[0] = -1; //probba o zgode
	msg[1] = queueNo;
	msg[2] = rank;
	msg[3] = haveCup;
	msg[4] = haveBottle;

	for (i = 0; i < size; i++) {
		if (i != rank) {
			logic_clock++;

			MPI_Send(&msg, 5, MPI_INT, i, MSGTAG, MPI_COMM_WORLD);
			printf("(%d) : Prosze o zgode %d\n", rank, i);
			fflush(stdout);
			sleep(DELAY);
		}
	}
}

int main(int argc, char* argv[]) {

	int i = 0; 
	
	bottles = 2; //liczba butelek
	cups = 1; //liczba kielichów
	haveBottle = 0;
	haveCup = 0;
	/* start up MPI */
	MPI_Init(&argc, &argv);

	/* find out process rank */
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);

	/* find out number of processes */
	MPI_Comm_size(MPI_COMM_WORLD, &size);

	pthread_t thread;
	pthread_attr_t pthread_custom_attr;

	waitingPhilosophers = malloc(size * sizeof(bool));
	waitingPhilosophersMessageID = malloc(size * sizeof(int));
	waitingPhilosophersRank = malloc(size * sizeof(int));
	waintingPhilosophersDeeper = malloc(size * sizeof(bool));

	requiredAgreementBottles = size - bottles;
	requiredAgreementCups = size - cups;
	printf("B: %d C: %d\n", requiredAgreementBottles, requiredAgreementCups);

	for (i = 0; i < size; i++) {
		waitingPhilosophers[i] = false;
		waitingPhilosophersMessageID[i] = 0;
		waitingPhilosophersRank[i] = 0;
		waintingPhilosophersDeeper[i] = false;
	}

	pthread_attr_init(&pthread_custom_attr);
	pthread_create(&thread, &pthread_custom_attr, receiver, NULL);

	while (1) {
		if (randomState) {
			want = wantToTake();
			
		}

		if (want == false) {
			printf("Filozof(%d) : Nie chce mi sie pic\n", rank);
		}
		if (want == true) {
			printf("Filozof(%d) : Chce mi sie pic \n", rank);
			queueNo = logic_clock + 1;
			randomState = false;

			sendToWaiting();
			tryToDrink();
		}
		//fflush(stdout);
		sleep(DELAY);
	}

	MPI_Finalize();

	return 0;
}
